﻿namespace Diplom.Graphics.D3D
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using Diplom.Graphics.D3D;
    using Diplom.Graphics.D3D.Helpers;
    using Diplom.NumericArrays;

    public static class IsolineVisualizer
    {
        #region Fields

        private static int countIso;
        private static double h;
        private static double gminZ;
        private static double gmaxZ;
        private static double epsilon = 1e-6;
        private static int width;
        private static int height;
        private static Dictionary<int, List<Edge>> isoline = new Dictionary<int, List<Edge>>();

        #endregion

        #region Methods

        private static void FillIsoline(Point3D px, Point3D py, Point3D pz, int n, int m)
        {
            if (Math.Abs(pz.X - pz.Y) < 1e-10 && Math.Abs(pz.X - pz.Z) < 1e-10) return;

            for (int i = 0; i < countIso; i++)
            {
                double xr1 = .0, yr1 = .0, xr2 = .0, yr2 = .0, xr3 = .0, yr3 = .0;
                Boolean f1 = false;
                Boolean f2 = false;
                Boolean f3 = false;
                double izo = gminZ + i * h;
                if ((Math.Abs(pz.X - pz.Y) > epsilon) && ((pz.X - izo) * (pz.Y - izo) <= -epsilon))
                {
                    f1 = true;
                    xr1 = px.X + (px.Y - px.X) / (pz.Y - pz.X) * (izo - pz.X);
                    yr1 = py.X + (py.Y - py.X) / (pz.Y - pz.X) * (izo - pz.X);                    
                }
                if ((Math.Abs(pz.Y - pz.Z) > epsilon) && ((pz.Z - izo) * (pz.Y - izo) <= -epsilon))
                {
                    f2 = true;
                    xr2 = px.Y + (px.Z - px.Y) / (pz.Z - pz.Y) * (izo - pz.Y);
                    yr2 = py.Y + (py.Z - py.Y) / (pz.Z - pz.Y) * (izo - pz.Y);                    
                }
                if ((Math.Abs(pz.Z - pz.X) > epsilon) && ((pz.X - izo) * (pz.Z - izo) <= -epsilon))
                {
                    f3 = true;
                    xr3 = px.Z + (px.X - px.Z) / (pz.X - pz.Z) * (izo - pz.Z);
                    yr3 = py.Z + (py.X - py.Z) / (pz.X - pz.Z) * (izo - pz.Z); 
                }

                if (f1 && f2)
                    isoline[i].Add(new Edge(xr1, yr1, xr2, yr2));
                if (f1 && f3)
                    isoline[i].Add(new Edge(xr1, yr1, xr3, yr3));
                if (f2 && f3)
                    isoline[i].Add(new Edge(xr2, yr2, xr3, yr3));
            }
        }

        public static void DrawIsoline(ref System.Drawing.Graphics canvas, System.Drawing.RectangleF workRect, ref NumericArrays.Numeric2DArray Sab, int CountIso)
        {   
            isoline = new Dictionary<int, List<Edge>>();
            gminZ = Sab.Min;
            gmaxZ = Sab.Max;
            countIso = CountIso;
            width = (int)workRect.Width;
            height = (int)workRect.Height;

            //canvas.DrawRectangle(new Pen(Brushes.Black, 2.0f),workRect.X,workRect.Y,workRect.Width,workRect.Height);
            
            h = (gmaxZ - gminZ) / (CountIso - 1);

            for (int i = 0; i < CountIso; i++)
            {
                isoline[i] = new List<Edge>();
            }

            int Na = Sab.NSize;
            int Nb = Sab.MSize;

            Point3D px = new Point3D();
            Point3D py = new Point3D();
            Point3D pz = new Point3D();

            for (int ia = 0; ia < Na - 1; ia++)
            {
                for (int ib = 0; ib < Nb - 1; ib++)
                {
                    px.X = px.Y = ib;
                    px.Z = ib + 1;
                    py.X = py.Z = ia;
                    py.Y = ia + 1;
                    pz.X = Sab.Items[ia][ib];
                    pz.Y = Sab.Items[ia + 1][ib];
                    pz.Z = Sab.Items[ia][ib + 1];
                    FillIsoline(px, py, pz, Nb, Na);

                    px.Z = px.Y = ib + 1;
                    px.X = ib;
                    py.X = py.Y = ia + 1;
                    py.Z = ia;
                    pz.X = Sab.Items[ia + 1][ib];
                    pz.Y = Sab.Items[ia + 1][ib + 1];
                    pz.Z = Sab.Items[ia][ib + 1];
                    FillIsoline(px, py, pz, Nb, Na);
                }
            }

            double xmi = Double.MaxValue;
            double ymi = Double.MaxValue;
            double xma = Double.MinValue;
            double yma = Double.MinValue;
            for (int i = 0; i < isoline.Count; i++)
            {
                for (int j = 0; j < isoline[i].Count; j++)
                {
                    xmi = Math.Min(isoline[i][j].a.X, Math.Min(isoline[i][j].b.X, xmi));
                    xma = Math.Max(isoline[i][j].a.X, Math.Max(isoline[i][j].b.X, xma));
                    ymi = Math.Min(isoline[i][j].a.Y, Math.Min(isoline[i][j].b.Y, ymi));
                    yma = Math.Max(isoline[i][j].a.Y, Math.Max(isoline[i][j].b.Y, yma));
                }
            }
            RectangleF functionRect = new RectangleF((float)xmi, (float)ymi, (float)(xma - xmi), (float)(yma - ymi));

            PointF begin;
            PointF current;

            for (int i = 0; i < isoline.Count; i++)
            {
                Color col = GenerateColor.GetColor(CountIso, i);
                for (int j = 0; j < isoline[i].Count; j++)
                {
                    /*begin = D2D.D2DVisualizer.Scalable(new PointF((float)(isoline[i][j].a.X),
                        (float)(functionRect.Height - isoline[i][j].a.Y)), functionRect, workRect);
                    current = D2D.D2DVisualizer.Scalable(new PointF((float)(isoline[i][j].b.X),
                        (float)(functionRect.Height - isoline[i][j].b.Y)), functionRect, workRect);*/
                    begin = D2D.D2DVisualizer.ScalableReverse(new PointF((float)(isoline[i][j].a.X),
                        (float)(isoline[i][j].a.Y)), functionRect, workRect);
                    
                    current = D2D.D2DVisualizer.ScalableReverse(new PointF((float)(isoline[i][j].b.X),
                        (float)(isoline[i][j].b.Y)), functionRect, workRect);
                    
                    canvas.DrawLine(new Pen(col), begin, current);
                }
            }
        }

        public static void DrawSignature(ref Graphics canvas, ref Numeric2DArray Z, RectangleF workRect, D2D.DrawStyle style, Size size)
        {
            Numeric1DArray array = new Numeric1DArray(2);
            array.TimeMin = Z.MinTime;
            array.TimeMax = Z.MaxTime;
            array.Min = Z.MaxFreak;
            //array.Min = Math.Pow(2.0, Z.MaxFreak);
            
            array.Max = Z.MinFreak;
            //array.Max = Math.Pow(2.0, Z.MinFreak);
            
            D2D.D2DVisualizer.DrawSignatureTwoPowers(ref canvas, ref array, workRect, style, new Size(size.Width,(int)Z.MaxFreak+1));
        }
     
        #endregion
    }
}